package ca.scotthyndman.as2haxe.dom.binding.impl;

import ca.scotthyndman.as2haxe.dom.ASFieldAccess;
import ca.scotthyndman.as2haxe.dom.ASName;
import ca.scotthyndman.as2haxe.dom.ASPackage;
import ca.scotthyndman.as2haxe.dom.ASQualifiedName;
import ca.scotthyndman.as2haxe.dom.ASSimpleName;
import ca.scotthyndman.as2haxe.dom.ASTNode;
import ca.scotthyndman.as2haxe.dom.ASType;
import ca.scotthyndman.as2haxe.dom.ASTypeDeclaration;
import ca.scotthyndman.as2haxe.dom.binding.ITypeBinding;

/**
 * Represents a binding to a type.
 */
public class TypeBinding extends BindingBase implements ITypeBinding {

	public TypeBinding(ASTNode node, String key) {
		super(node, key);
	}

	public BindingKind getKind() {
		return BindingKind.TYPE;
	}

	public String rename(ASSimpleName newName) throws Exception {
		ASTypeDeclaration declr = (ASTypeDeclaration) getASTNode();
		ASQualifiedName newQName = new ASQualifiedName(declr.getAST(),
				(ASSimpleName) newName.clone());
		if ((ASQualifiedName) declr.getName().getQualifier() != null) {
			newQName.setQualifier((ASQualifiedName) declr
					.getName().getQualifier().clone());
		}

		//
		// Throw an exception if there is a name overlap
		//
		// FIXME had to comment this out when I removed package from the type
//		if (declr.getPackage().getTypesMap().containsKey(newName.toString())) {
//			throw new Exception("Cannot rename " + declr.getName().toString()
//					+ " to " + newQName.toString()
//					+ ". A type with that name already exists.");
//		}

		//
		// Log
		//
		if (log.isInfoEnabled()) {
			log.info("Renaming " + declr.getName().toString() + " to "
					+ newQName.toString());
		}

		//
		// Change the source node
		//
		declr.setName(newQName);

		//
		// Change the referrers
		//
		for (ASTNode node : getReferringNodes()) {
			ASSimpleName clone = (ASSimpleName) newName.clone();
			if (node instanceof ASType) {
				ASType type = (ASType) node;
				if (type.getName().isQualifiedName()) {
					((ASQualifiedName) type.getName()).setName(clone);
				} else {
					type.setName(clone);
				}
			} else if (node instanceof ASFieldAccess) {
				((ASFieldAccess) node).setName(clone);
			}

		}
		
		//
		// Return the binding's new key
		//
		return newQName.toString();
	}

	public String move(ASTNode newParent) throws Exception {
		ASTypeDeclaration type = (ASTypeDeclaration) getASTNode();

		//
		// Test new parent
		//
		if (!(newParent instanceof ASPackage)) {
			throw new IllegalArgumentException(
					"ASPackage is the only acceptable parent for "
							+ "an ASTypeDeclaration. type: "
							+ type.getName().toString());
		}
		
		ASPackage pkg = (ASPackage) newParent;

		//
		// Throw an exception if there is a name overlap
		//
		if (pkg.getTypesMap().containsKey(type.getName().getName().toString())) {
			throw new Exception("Cannot move " + type.getName().toString()
					+ " to " + pkg.toString()
					+ ". A type with that name already exists.");
		}

		//
		// Create the new name
		//
		ASQualifiedName newName = new ASQualifiedName(type.getAST(),
				(ASSimpleName) type.getName().getName().clone());
		newName.setQualifier((ASName) pkg.getName().clone());

		//
		// Log
		//
		if (log.isInfoEnabled()) {
			log.info("Moving " + type.getName().toString() + " to "
					+ pkg.getName().toString());
		}

		//
		// Change the source node
		//
		type.setName(newName);

		//
		// Change the referrers
		//
		for (ASTNode node : getReferringNodes()) {
			ASQualifiedName clone = (ASQualifiedName) newName.clone();
			if (node instanceof ASType) {
				ASType t = (ASType) node;
				if (t.getName().isQualifiedName()) {
					t.setName(clone);
				} else {
					t.setName(clone);
				}
			} else if (node instanceof ASFieldAccess) {
				// FIXME how do we deal with package fields
				((ASFieldAccess) node).setName(clone.getName());
			}

		}
		
		return newName.toString();
	}

}
